Popular Searches
Popular Course Categories
Popular Courses

Responsive UI in Flutter

Responsive UI in Flutter

Flutter Responsive Design


Responsive UI in Flutter


Responsive UI in Flutter means designing an application so that its interface automatically adjusts to the available screen space. A responsive Flutter application should work comfortably across phones, tablets, desktops, web browsers, foldable devices, and resizable windows.


Flutter distinguishes between responsive design, which focuses on fitting UI elements into available space, and adaptive design, which focuses on making the UI usable in that space. In practical Flutter development, both concepts are commonly used together. Flutter's official adaptive and responsive design guide explains this approach in detail.


1. Why Responsive UI Is Important


Users can access the same application from devices with very different screen sizes. A layout that looks good on a mobile phone may become too stretched, crowded, or difficult to use on a tablet or desktop.


Responsive design helps an application:



  • Use available screen space effectively.

  • Prevent content from overflowing.

  • Improve readability.

  • Support portrait and landscape layouts.

  • Provide better tablet and desktop experiences.

  • Support resizable application windows.

  • Make navigation appropriate for the available width.

  • Improve accessibility and usability.


2. Responsive vs Adaptive UI








Responsive UIAdaptive UI
Adjusts the layout to available space.Changes the UI structure or interaction model when appropriate.
May resize, wrap, or reposition widgets.May switch between different navigation patterns.
Focuses on fitting content.Focuses on making the interface usable.
Example: changing grid columns.Example: changing bottom navigation to a navigation rail.

3. Common Responsive Design Strategies



  • Use flexible widgets such as Expanded and Flexible.

  • Use LayoutBuilder to respond to local layout constraints.

  • Use MediaQuery.sizeOf() when the decision depends on the application window size.

  • Use SafeArea to avoid system UI and display cutouts.

  • Use Wrap for content that should move onto another line.

  • Use responsive grids.

  • Use maximum content widths on large screens.

  • Use different navigation layouts at appropriate breakpoints.

  • Avoid hard-coding a single device width.

  • Test the application at different window sizes.


4. Understanding Logical Pixels


Flutter layouts work with logical pixels rather than directly using physical screen pixels. This allows UI elements to maintain a more consistent visual size across different displays.


Container(
  width: 200,
  height: 100,
  child: const Text('Responsive Box'),
)

However, fixed dimensions should be used carefully. A fixed width may work on one screen and create overflow or excessive empty space on another.


5. Using Expanded for Responsive Width


Expanded allows a child of a Row or Column to occupy available remaining space.


Row(
  children: [
    Expanded(
      child: Container(
        height: 100,
        child: const Center(
          child: Text('Left'),
        ),
      ),
    ),
    Expanded(
      child: Container(
        height: 100,
        child: const Center(
          child: Text('Right'),
        ),
      ),
    ),
  ],
)

Both containers share the available horizontal space instead of using fixed widths.


6. Using Flexible


Flexible is useful when a widget should be allowed to shrink or use available space without necessarily forcing it to fill all remaining space.


Row(
  children: [
    Flexible(
      child: Text(
        'This is a long piece of text that can adapt to available width.',
      ),
    ),
    const Icon(Icons.info),
  ],
)

7. Using MediaQuery.sizeOf()


MediaQuery.sizeOf(context) provides the current application window size. Flutter's current adaptive-design guidance recommends using MediaQuery.sizeOf when a responsive decision should be based on the overall app window. Flutter's general adaptive approach explains when to use MediaQuery.sizeOf and when to use LayoutBuilder.


final size = MediaQuery.sizeOf(context);

final width = size.width;
final height = size.height;


Example


class ResponsiveScreen extends StatelessWidget {
  const ResponsiveScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;

    return Scaffold(
      body: Center(
        child: Text(
          'Width: $width',
          style: const TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}


8. Why Use LayoutBuilder?


LayoutBuilder provides the constraints available to the widget from its parent. This makes it useful when a particular widget needs to adapt according to the space assigned to it rather than the entire application window.


LayoutBuilder(
  builder: (context, constraints) {
    return Text(
      'Available width: ${constraints.maxWidth}',
    );
  },
)

The constraints object contains values such as:



  • minWidth

  • maxWidth

  • minHeight

  • maxHeight


9. Responsive Layout with LayoutBuilder


class ResponsiveHome extends StatelessWidget {
  const ResponsiveHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) {
          if (constraints.maxWidth < 600) {
            return const MobileLayout();
          }

          return const TabletLayout();
        },
      ),
    );
  }
}

class MobileLayout extends StatelessWidget {
  const MobileLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Mobile Layout'),
    );
  }
}

class TabletLayout extends StatelessWidget {
  const TabletLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Tablet Layout'),
    );
  }
}


The important principle is that the layout is selected based on available width rather than simply assuming that a particular device type always has a particular size.


10. Responsive Breakpoints


A breakpoint is a width at which the application changes its layout strategy.


For example:







Available WidthPossible Layout
Less than 600Compact mobile layout
600 to 1023Tablet or medium layout
1024 and aboveLarge tablet/desktop layout

These numbers are examples for a project, not universal device rules. Flutter's adaptive guidance recommends choosing breakpoints based on when your UI needs to change rather than identifying a device as a phone or tablet.


11. Creating a Breakpoint Helper


class AppBreakpoints {
  static const double mobile = 600;
  static const double tablet = 1024;
}

Use it in a responsive widget:


LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < AppBreakpoints.mobile) {
      return const MobileLayout();
    }

    if (constraints.maxWidth < AppBreakpoints.tablet) {
      return const TabletLayout();
    }

    return const DesktopLayout();
  },
)


12. Responsive Row and Column


A common responsive technique is to display content horizontally on large screens and vertically on small screens.


LayoutBuilder(
  builder: (context, constraints) {
    final isWide = constraints.maxWidth >= 700;

    return Flex(
      direction: isWide
          ? Axis.horizontal
          : Axis.vertical,
      children: [
        Expanded(
          child: Container(
            padding: const EdgeInsets.all(20),
            child: const Text('Section 1'),
          ),
        ),
        Expanded(
          child: Container(
            padding: const EdgeInsets.all(20),
            child: const Text('Section 2'),
          ),
        ),
      ],
    );
  },
)


13. Responsive GridView


Grid layouts are frequently used for dashboards, product listings, image galleries, and cards.


LayoutBuilder(
  builder: (context, constraints) {
    int columns;

    if (constraints.maxWidth < 600) {
      columns = 2;
    } else if (constraints.maxWidth < 1000) {
      columns = 3;
    } else {
      columns = 4;
    }

    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: columns,
        crossAxisSpacing: 12,
        mainAxisSpacing: 12,
      ),
      itemCount: 20,
      itemBuilder: (context, index) {
        return Card(
          child: Center(
            child: Text('Item ${index + 1}'),
          ),
        );
      },
    );
  },
)


14. Responsive Product Grid


class Product {
  final String name;
  final double price;

  Product({
    required this.name,
    required this.price,
  });
}

final products = [
  Product(name: 'Laptop', price: 65000),
  Product(name: 'Phone', price: 30000),
  Product(name: 'Tablet', price: 25000),
  Product(name: 'Watch', price: 5000),
  Product(name: 'Camera', price: 45000),
  Product(name: 'Headphones', price: 2500),
];


LayoutBuilder(
  builder: (context, constraints) {
    int columns = 2;

    if (constraints.maxWidth >= 600) {
      columns = 3;
    }

    if (constraints.maxWidth >= 1000) {
      columns = 4;
    }

    return GridView.builder(
      padding: const EdgeInsets.all(16),
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: columns,
        crossAxisSpacing: 12,
        mainAxisSpacing: 12,
        childAspectRatio: 1.2,
      ),
      itemCount: products.length,
      itemBuilder: (context, index) {
        final product = products[index];

        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Icon(
                  Icons.shopping_bag,
                  size: 40,
                ),
                const SizedBox(height: 10),
                Text(
                  product.name,
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 5),
                Text('₹${product.price}'),
              ],
            ),
          ),
        );
      },
    );
  },
)


15. Responsive Navigation


Navigation is one of the most important areas of responsive UI. A small window may use a bottom navigation bar, while a wider window can use a navigation rail or side navigation.


LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 600) {
      return Scaffold(
        body: const Center(
          child: Text('Mobile Content'),
        ),
        bottomNavigationBar: NavigationBar(
          destinations: const [
            NavigationDestination(
              icon: Icon(Icons.home),
              label: 'Home',
            ),
            NavigationDestination(
              icon: Icon(Icons.person),
              label: 'Profile',
            ),
          ],
        ),
      );
    }

    return Scaffold(
      body: Row(
        children: [
          NavigationRail(
            destinations: const [
              NavigationRailDestination(
                icon: Icon(Icons.home),
                label: Text('Home'),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.person),
                label: Text('Profile'),
              ),
            ],
            selectedIndex: 0,
          ),
          const Expanded(
            child: Center(
              child: Text('Large Screen Content'),
            ),
          ),
        ],
      ),
    );
  },
)


Flutter's adaptive design guidance uses this type of approach: navigation can change according to available window width rather than assuming a particular hardware category.


16. Responsive Sidebar Layout


A common desktop design uses a sidebar and a main content area. On a small screen, the sidebar can be replaced by another navigation pattern.


LayoutBuilder(
  builder: (context, constraints) {
    final isLarge = constraints.maxWidth >= 800;

    if (isLarge) {
      return Row(
        children: [
          SizedBox(
            width: 250,
            child: Container(
              padding: const EdgeInsets.all(20),
              child: const Text('Sidebar'),
            ),
          ),
          const Expanded(
            child: Center(
              child: Text('Main Content'),
            ),
          ),
        ],
      );
    }

    return const Center(
      child: Text('Mobile Content'),
    );
  },
)


Flutter's adaptive-layout learning material demonstrates a similar large-screen pattern using a fixed-width sidebar and an Expanded detail area.


17. Responsive Dashboard


A dashboard can change the number of columns based on available width.


class Dashboard extends StatelessWidget {
  const Dashboard({super.key});

  @override
  Widget build(BuildContext context) {
    final items = [
      ('Users', '12,450', Icons.people),
      ('Orders', '3,240', Icons.shopping_cart),
      ('Revenue', '₹8,45,000', Icons.currency_rupee),
      ('Pending', '128', Icons.pending),
    ];

    return LayoutBuilder(
      builder: (context, constraints) {
        final columns = constraints.maxWidth < 600
            ? 1
            : constraints.maxWidth < 1000
                ? 2
                : 4;

        return GridView.builder(
          padding: const EdgeInsets.all(16),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: columns,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 1.5,
          ),
          itemCount: items.length,
          itemBuilder: (context, index) {
            final item = items[index];

            return Card(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(item.$3, size: 35),
                  const SizedBox(height: 10),
                  Text(item.$1),
                  Text(
                    item.$2,
                    style: const TextStyle(
                      fontSize: 22,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            );
          },
        );
      },
    );
  }
}


18. Using Wrap for Responsive Content


Wrap automatically moves children to another line when there is not enough horizontal space.


Wrap(
  spacing: 10,
  runSpacing: 10,
  children: [
    Chip(label: Text('Flutter')),
    Chip(label: Text('Dart')),
    Chip(label: Text('Firebase')),
    Chip(label: Text('UI Design')),
    Chip(label: Text('API')),
    Chip(label: Text('Database')),
  ],
)

This is useful for tags, filters, buttons, categories, and other content with variable widths.


19. Responsive Text


Text should be designed to work across different widths. Avoid forcing long text into a small fixed-width container.


Expanded(
  child: Text(
    'Flutter makes it possible to build responsive applications from a single codebase.',
    maxLines: 3,
    overflow: TextOverflow.ellipsis,
  ),
)

20. Responsive Font Size


Font sizes can be adjusted when there is a genuine design requirement, but excessive device-specific scaling should be avoided.


LayoutBuilder(
  builder: (context, constraints) {
    final titleSize = constraints.maxWidth < 600
        ? 24.0
        : 32.0;

    return Text(
      'Responsive Heading',
      style: TextStyle(
        fontSize: titleSize,
        fontWeight: FontWeight.bold,
      ),
    );
  },
)


21. Responsive Padding


Large screens often benefit from additional horizontal margins, while smaller screens need compact spacing.


LayoutBuilder(
  builder: (context, constraints) {
    final padding = constraints.maxWidth < 600
        ? 16.0
        : 32.0;

    return Padding(
      padding: EdgeInsets.symmetric(
        horizontal: padding,
      ),
      child: const Text(
        'Responsive content area',
      ),
    );
  },
)


22. Limiting Content Width on Large Screens


Using the entire width of a large desktop screen for text can make reading uncomfortable. A maximum content width can provide a better reading area.


Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 900,
    ),
    child: const Padding(
      padding: EdgeInsets.all(24),
      child: Text(
        'This content remains within a readable maximum width.',
      ),
    ),
  ),
)

23. Using ConstrainedBox


ConstrainedBox allows developers to control the minimum and maximum dimensions of a widget.


ConstrainedBox(
  constraints: const BoxConstraints(
    maxWidth: 500,
  ),
  child: TextField(
    decoration: const InputDecoration(
      labelText: 'Email',
      border: OutlineInputBorder(),
    ),
  ),
)

24. Responsive Forms


Forms should not become extremely wide on desktop screens.


Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 500,
    ),
    child: Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        children: [
          TextField(
            decoration: const InputDecoration(
              labelText: 'Name',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 16),
          TextField(
            decoration: const InputDecoration(
              labelText: 'Email',
              border: OutlineInputBorder(),
            ),
          ),
        ],
      ),
    ),
  ),
)

25. Responsive Two-Column Form


LayoutBuilder(
  builder: (context, constraints) {
    final isWide = constraints.maxWidth >= 700;

    if (isWide) {
      return Row(
        children: [
          Expanded(
            child: TextField(
              decoration: const InputDecoration(
                labelText: 'First Name',
                border: OutlineInputBorder(),
              ),
            ),
          ),
          const SizedBox(width: 16),
          Expanded(
            child: TextField(
              decoration: const InputDecoration(
                labelText: 'Last Name',
                border: OutlineInputBorder(),
              ),
            ),
          ),
        ],
      );
    }

    return Column(
      children: [
        TextField(
          decoration: const InputDecoration(
            labelText: 'First Name',
            border: OutlineInputBorder(),
          ),
        ),
        const SizedBox(height: 16),
        TextField(
          decoration: const InputDecoration(
            labelText: 'Last Name',
            border: OutlineInputBorder(),
          ),
        ),
      ],
    );
  },
)


26. SafeArea


SafeArea helps keep important content away from system UI, display cutouts, and areas such as status bars. Flutter's official documentation recommends using it around content that should not be obscured by these areas. Learn about SafeArea and MediaQuery.


Scaffold(
  body: SafeArea(
    child: Column(
      children: [
        const Text('Header'),
        Expanded(
          child: ListView(
            children: const [
              ListTile(title: Text('Item 1')),
              ListTile(title: Text('Item 2')),
            ],
          ),
        ),
      ],
    ),
  ),
)

27. Responsive Images


Images should adapt to the available space instead of relying on fixed dimensions.


AspectRatio(
  aspectRatio: 16 / 9,
  child: Image.network(
    'https://example.com/image.jpg',
    fit: BoxFit.cover,
  ),
)

For responsive images inside a row:


Expanded(
  child: Image.network(
    'https://example.com/product.jpg',
    fit: BoxFit.cover,
  ),
)

28. Responsive Dialogs


Dialogs can use a maximum width so that they do not become unnecessarily wide on desktop screens.


showDialog(
  context: context,
  builder: (context) {
    return Dialog(
      child: ConstrainedBox(
        constraints: const BoxConstraints(
          maxWidth: 500,
        ),
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                'Responsive Dialog',
                style: TextStyle(
                  fontSize: 22,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 20),
              const Text(
                'This dialog has a maximum width.',
              ),
            ],
          ),
        ),
      ),
    );
  },
)

29. Responsive List and Detail Layout


A common adaptive application pattern is to display a list and details side-by-side on large screens while showing one screen at a time on smaller screens.


LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth >= 800) {
      return Row(
        children: [
          SizedBox(
            width: 300,
            child: UserList(),
          ),
          const VerticalDivider(width: 1),
          const Expanded(
            child: UserDetails(),
          ),
        ],
      );
    }

    return const UserList();
  },
)


This pattern is useful for email applications, contact applications, messaging applications, administration systems, and document managers.


30. Orientation and Responsive Design


Applications can be displayed in portrait or landscape mode, but layout decisions should generally be based on available space rather than assuming that portrait always means phone and landscape always means tablet or desktop.


Flutter's adaptive-design guidance recommends using window size or local constraints when deciding how the UI should be structured. Flutter adaptive design best practices discusses why device type and orientation are not reliable substitutes for available layout space.


31. Responsive Layout with OrientationBuilder


OrientationBuilder can still be useful when the actual orientation is specifically relevant to the design.


OrientationBuilder(
  builder: (context, orientation) {
    return GridView.count(
      crossAxisCount:
          orientation == Orientation.portrait ? 2 : 4,
      children: const [
        Card(child: Center(child: Text('1'))),
        Card(child: Center(child: Text('2'))),
        Card(child: Center(child: Text('3'))),
        Card(child: Center(child: Text('4'))),
      ],
    );
  },
)

For general adaptive layout decisions, prefer available window size or parent constraints rather than using orientation alone.


32. Responsive AppBar


LayoutBuilder(
  builder: (context, constraints) {
    final isLarge = constraints.maxWidth >= 700;

    return AppBar(
      title: const Text('My Application'),
      actions: [
        if (isLarge) ...[
          TextButton(
            onPressed: () {},
            child: const Text('Home'),
          ),
          TextButton(
            onPressed: () {},
            child: const Text('About'),
          ),
        ],
        IconButton(
          onPressed: () {},
          icon: const Icon(Icons.person),
        ),
      ],
    );
  },
)


33. Responsive Visibility


Sometimes different widgets should appear at different widths.


LayoutBuilder(
  builder: (context, constraints) {
    final isMobile = constraints.maxWidth < 600;

    return Column(
      children: [
        const Text('Common Header'),
        if (isMobile)
          const Text('Mobile Actions')
        else
          const Text('Desktop Actions'),
      ],
    );
  },
)


34. Responsive Spacing


Spacing can also be adapted to available space.


LayoutBuilder(
  builder: (context, constraints) {
    final spacing = constraints.maxWidth < 600
        ? 12.0
        : 24.0;

    return Column(
      children: [
        const Text('First Section'),
        SizedBox(height: spacing),
        const Text('Second Section'),
      ],
    );
  },
)


35. Building a Complete Responsive Screen


import 'package:flutter/material.dart';

class ResponsiveHome extends StatelessWidget {
  const ResponsiveHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Responsive Dashboard'),
      ),
      body: SafeArea(
        child: LayoutBuilder(
          builder: (context, constraints) {
            final width = constraints.maxWidth;

            int columns;

            if (width < 600) {
              columns = 1;
            } else if (width < 1000) {
              columns = 2;
            } else {
              columns = 4;
            }

            return Center(
              child: ConstrainedBox(
                constraints: const BoxConstraints(
                  maxWidth: 1400,
                ),
                child: GridView.builder(
                  padding: const EdgeInsets.all(16),
                  gridDelegate:
                      SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: columns,
                    crossAxisSpacing: 16,
                    mainAxisSpacing: 16,
                    childAspectRatio: 1.5,
                  ),
                  itemCount: 8,
                  itemBuilder: (context, index) {
                    return Card(
                      child: Center(
                        child: Text(
                          'Dashboard Card ${index + 1}',
                          textAlign: TextAlign.center,
                        ),
                      ),
                    );
                  },
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}


36. How the Complete Example Works



  1. Scaffold provides the basic application structure.

  2. SafeArea protects content from system UI.

  3. LayoutBuilder measures the available width.

  4. The number of grid columns changes according to available width.

  5. ConstrainedBox prevents the dashboard from becoming excessively wide.

  6. GridView.builder creates dashboard cards dynamically.

  7. The same code can respond to different window sizes without creating separate applications.


37. Avoiding Overflow


One of the most common problems in Flutter responsive layouts is the RenderFlex overflowed error.


Problem


Row(
  children: [
    const Text(
      'This is a very long text that may overflow',
    ),
    const Icon(Icons.info),
  ],
)

Better Solution


Row(
  children: [
    const Expanded(
      child: Text(
        'This is a very long text that can use available space.',
      ),
    ),
    const Icon(Icons.info),
  ],
)

38. Common Responsive UI Mistakes



  • Using fixed widths everywhere.

  • Assuming every phone has the same screen width.

  • Assuming landscape always means tablet or desktop.

  • Using device type instead of available window size for layout decisions.

  • Forgetting SafeArea when content may be obscured.

  • Allowing text to overflow.

  • Making desktop forms unnecessarily wide.

  • Using too many columns on small screens.

  • Using very few columns on large screens.

  • Ignoring keyboard, mouse, and touch interaction requirements.

  • Not testing resizable windows.


39. Responsive UI Best Practices



  • Design for available space rather than a specific device name.

  • Use LayoutBuilder when a widget needs to respond to its parent's constraints.

  • Use MediaQuery.sizeOf() when the layout decision depends on the overall application window.

  • Use Expanded and Flexible for flexible layouts.

  • Use Wrap for variable-width content.

  • Use ConstrainedBox or maximum widths for large-screen content.

  • Use responsive grid column counts.

  • Use appropriate navigation patterns for different window sizes.

  • Use SafeArea where content must avoid system UI.

  • Keep widgets small and reusable.

  • Test different widths, heights, orientations, and window configurations.

  • Support touch, mouse, keyboard, and trackpad interactions where appropriate.

  • Do not lock orientation simply to avoid handling responsive layouts.


40. Testing Responsive UI


Responsive interfaces should be tested at multiple sizes rather than only on one physical device.


Testing Checklist



  • Small mobile width

  • Large mobile width

  • Portrait orientation

  • Landscape orientation

  • Tablet-sized window

  • Desktop-sized window

  • Resizable browser window

  • Large monitor

  • Different text scaling settings

  • Keyboard and mouse input

  • Touch interaction

  • Long text and localized content


41. Responsive UI Architecture


A maintainable responsive application can separate common data and widgets from layout-specific arrangements.


lib/
├── main.dart
├── models/
│   └── product.dart
├── screens/
│   └── home_screen.dart
├── widgets/
│   ├── product_card.dart
│   ├── mobile_layout.dart
│   ├── tablet_layout.dart
│   └── desktop_layout.dart
└── utils/
    └── breakpoints.dart

The same data and reusable components can then be used in multiple layouts.


42. Responsive Design Example: Mobile, Tablet, Desktop


class AdaptiveHome extends StatelessWidget {
  const AdaptiveHome({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final width = constraints.maxWidth;

        if (width < 600) {
          return const MobileHome();
        }

        if (width < 1024) {
          return const TabletHome();
        }

        return const DesktopHome();
      },
    );
  }
}

class MobileHome extends StatelessWidget {
  const MobileHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('Mobile Home'),
      ),
    );
  }
}

class TabletHome extends StatelessWidget {
  const TabletHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('Tablet Home'),
      ),
    );
  }
}

class DesktopHome extends StatelessWidget {
  const DesktopHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('Desktop Home'),
      ),
    );
  }
}


43. Key Widgets for Responsive Flutter UI














Widget/APIPurpose
LayoutBuilderRespond to the constraints provided by a parent widget.
MediaQuery.sizeOfRead the current application window size.
ExpandedFill available space in Row or Column.
FlexibleAllow children to flex within available space.
WrapMove children to additional lines when necessary.
SafeAreaProtect content from system UI and display cutouts.
ConstrainedBoxApply minimum and maximum layout constraints.
GridView.builderCreate dynamic responsive grids.
NavigationBarCompact navigation suitable for smaller layouts.
NavigationRailNavigation pattern useful for wider layouts.

44. Quick Revision



  • Responsive UI adjusts the interface according to available space.

  • Adaptive UI can change the structure or interaction model according to available space.

  • LayoutBuilder works with parent constraints.

  • MediaQuery.sizeOf() provides the overall application window size.

  • Expanded and Flexible help create flexible layouts.

  • Wrap helps content move onto additional lines.

  • SafeArea protects important content from system UI.

  • Responsive grids can change their column count.

  • Navigation can change between compact and large-screen patterns.

  • Large-screen content should often have a maximum width.

  • Responsive decisions should generally be based on available space rather than device names.

  • Always test layouts at multiple window sizes.


45. Practice Exercises



  1. Create a responsive profile screen that changes from one column to two columns.

  2. Create a product grid with 2 columns on small screens, 3 on medium screens, and 4 on large screens.

  3. Create a responsive dashboard with dynamic cards.

  4. Create a mobile bottom navigation and desktop navigation rail.

  5. Create a responsive login form with a maximum width.

  6. Create a responsive sidebar and content layout.

  7. Create a responsive image gallery using GridView.builder.

  8. Create a responsive settings screen.

  9. Create a responsive student management dashboard.

  10. Resize a Flutter web application and verify that the layout adapts correctly.


46. Conclusion


Responsive UI is an essential Flutter skill for building applications that work across different screen sizes and form factors. Flutter provides powerful tools such as LayoutBuilder, MediaQuery.sizeOf(), Expanded, Flexible, Wrap, SafeArea, ConstrainedBox, and responsive navigation widgets.


The most important principle is to design according to the space available to the application, rather than making assumptions about whether the user is holding a phone, tablet, or desktop device. A well-designed responsive Flutter application can reuse the same data and components while changing their arrangement to provide an appropriate experience at different sizes.


Official Flutter Resources



Learn Flutter with JustAcademy



whatsapp